package com.nowcoder.topic.dp.middle;

import java.util.ArrayList;

/**
 * NC385 划分等和序列
 * @author d3y1
 */
public class NC385{
    private boolean[] visited;
    private int avg;

    /**
     * 代码中的类名、方法名、参数名已经指定，请勿修改，直接返回方法规定的值即可
     *
     *
     * @param nums int整型ArrayList
     * @param k int整型
     * @return bool布尔型
     */
    public boolean candivide (ArrayList<Integer> nums, int k) {
        return solution1(nums, k);
//        return solution2(nums, k);
    }

    /**
     * 动态规划: 01背包
     *
     * dp[j]表示背包大小为j时能够组成的最大的和(数组中的元素和)[数组中每个元素仅用一次]
     *
     * dp[j] = Math.max(dp[j], dp[j-num] + num)
     *
     * @param nums
     * @param k
     * @return
     */
    private boolean solution1(ArrayList<Integer> nums, int k){
        int n = nums.size();

        int sum = 0;
        for(int i=0; i<n; i++){
            sum += nums.get(i);
        }

        if(sum%k != 0){
            return false;
        }
        avg = sum/k;

        int[] dp = new int[sum+1];
        int num;
        for(int i=0; i<n; i++){
            num = nums.get(i);
            if(num > avg){
                return false;
            }
            // 数组中每个元素仅用一次 -> 降序
            for(int j=sum; j>=num; j--){
                dp[j] = Math.max(dp[j], dp[j-num] + num);
            }
        }

        // avg的倍数 均要判断
        for(int i=1; i<=k; i++){
            // 恰好装满 -> 恰好组成下一个元素和avg
            if(dp[avg*i] != avg*i){
                return false;
            }
        }

        return true;
    }

    /**
     * dfs: 遍历解空间
     * @param nums
     * @param k
     * @return
     */
    private boolean solution2(ArrayList<Integer> nums, int k){
        int n = nums.size();
        visited = new boolean[n];

        int sum = 0;
        for(int i=0; i<n; i++){
            sum += nums.get(i);
        }

        if(sum%k != 0){
            return false;
        }
        int avg = sum/k;

        return dfs(nums, k, avg);
    }

    /**
     * dfs: 递归
     * @param nums
     * @param k
     * @param target
     * @return
     */
    private boolean dfs(ArrayList<Integer> nums, int k, int target){
        if(k == 0){
            return true;
        }
        if(target == 0){
            if(dfs(nums, k-1, avg)){
                return true;
            }
        }else if(target < 0){
            return false;
        }else{
            for(int i=0; i<nums.size(); i++){
                if(!visited[i]){
                    visited[i] = true;
                    if(dfs(nums, k, target-nums.get(i))){
                        return true;
                    }
                    visited[i] = false;
                }
            }
        }

        return false;
    }
}